/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2009 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/

#include "../SDL_cellvideo.h"
#include "../../../events/SDL_events_c.h"

#include <cell/pad.h>

/* Captions */
#define BUILDING_CELLPADMAP
#include "SDL_cellinputimgs.h"
#undef BUILDING_CELLPADMAP

/* Data for image mode indicator */
#ifndef CELL_NO_SOFT_INPUT
typedef void					(*ModeFunction)			(enum PadMode);
static const ModeFunction		ModeEnter[3]			= {CELL_PAD_JOYSTICK_Enter, CELL_PAD_MOUSE_Enter,	CELL_PAD_KEYBOARD_Enter};
static const ModeFunction		ModeExit[3]				= {CELL_PAD_JOYSTICK_Exit,	CELL_PAD_MOUSE_Exit,	CELL_PAD_KEYBOARD_Exit};
static const int32_t			JoystickMap[16]			= {10, -1, -1, 9, 11, 12, 13, 14, 4, 5, 6, 7, 3, 2, 0, 1};
static const int32_t			NullifyMap[16]			= {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#else
static const int32_t			JoystickMap[16]			= {11, 8, 9, 10, 12, 13, 14, 15, 4, 5, 6, 7, 3, 2, 0, 1};
#endif

/* Helpers */
static void						InitPads				()
{
#ifndef CELL_NO_SOFT_INPUT
	if(!CELL_Video.Pads.InputIcons[0].TextureID)
	{
		for(int i = 0; i != 16; i ++)
		{
			CELL_Video.Pads.ButtonMap[i].Joystick = JoystickMap[i];
			CELL_Video.Pads.ButtonMap[i].Nullify = NullifyMap[i];
		}

		/* Load resources */
		CELL_IMAGE_Load(&CELL_Video.Pads.InputIcons[0], 32, 32, 64, 64, (void*)CELL_joystickimg, 32, 32);
		CELL_IMAGE_Load(&CELL_Video.Pads.InputIcons[1], 32, 32, 64, 64, (void*)CELL_mouseimg, 32, 32);
		CELL_IMAGE_Load(&CELL_Video.Pads.InputIcons[2], 32, 32, 64, 64, (void*)CELL_keyboardimg, 32, 32);

		/* Open any joysticks that were opened before this was called */
		for(int i = 0; i != NUM_PADS; i ++)
		{
			CELL_PAD_SetOpen(CELL_Video.Pads.Pads[i].Joystick, i, CELL_Video.Pads.Pads[i].Open);
		}

		/* We start in mouse mode */
		CELL_Video.Pads.Pads[0].Mode = MOUSE;
	}
#else
		for(int i = 0; i != 16; i ++)
		{
			CELL_Video.Pads.ButtonMap[i].Joystick = JoystickMap[i];
			CELL_Video.Pads.ButtonMap[i].Nullify = 0;
		}
#endif
}

#ifndef CELL_NO_SOFT_INPUT
static void						DrawInputIcons						()
{
	/* Don't draw if we timed out */
	if(CELL_Video.Pads.InputIconHideTime > SDL_GetTicks())
	{
		/* Set projection */
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glOrthof(0, 640, 480, 0, -1, 1);

		if(CELL_Video.Pads.Pads[0].Open)
		{
			int32_t currentIcon = ((int32_t)CELL_Video.Pads.Pads[0].Mode) + 3;

			/* Draw images, skipping joystick if need be */
			for(int i = 0; i != 3; i ++)
			{
				CELL_IMAGE_Draw_TA(&CELL_Video.Pads.InputIcons[(currentIcon + i - 1) % 3], 64 * i, 0, (i == 1) ? .75f : .25f);
			}
		}
		else
		{
			enum PadMode current = CELL_Video.Pads.Pads[0].Mode;
			CELL_IMAGE_Draw_TA(&CELL_Video.Pads.InputIcons[(current == MOUSE) ? 2 : 1], 64 * 0, 0, .25f);
			CELL_IMAGE_Draw_TA(&CELL_Video.Pads.InputIcons[(current == MOUSE) ? 1 : 2], 64 * 1, 0, .75f);
			CELL_IMAGE_Draw_TA(&CELL_Video.Pads.InputIcons[(current == MOUSE) ? 2 : 1], 64 * 2, 0, .25f);
		}

		/* Reset projection */
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
	}
}

static void						UpdateMode							()
{
	static uint32_t buttontaken = 0;
	const uint32_t maxmode = 3;
	static uint32_t updatetime = 0;

	if(!buttontaken && (CELL_Video.Pads.Pads[0].OldButtons & 6))
	{
		if(!updatetime)
		{
			updatetime = SDL_GetTicks();
		}
		else if(SDL_GetTicks() - updatetime > 250)
		{
			SDL_ResetKeyboard();
			CELL_ReleaseAllMouseButtons();

			int32_t thismode = CELL_Video.Pads.Pads[0].Mode + maxmode;

			if(CELL_Video.Pads.Pads[0].Open)
			{
				thismode += (CELL_Video.Pads.Pads[0].OldButtons & 4) ? 1 : 0;
				thismode -= (CELL_Video.Pads.Pads[0].OldButtons & 2) ? 1 : 0;
			}
			else
			{
				thismode = (CELL_Video.Pads.Pads[0].Mode != MOUSE) ? MOUSE : KEYBOARD;
			}

			thismode %= maxmode;

			if(thismode != CELL_Video.Pads.Pads[0].Mode)
			{
				CELL_PAD_SetMode(0, (enum PadMode)thismode);
				CELL_Video.Pads.InputIconHideTime = SDL_GetTicks() + 5000;

				buttontaken = 2;
			}

			updatetime = 0;
		}
	}
	else if(buttontaken && !(CELL_Video.Pads.Pads[0].OldButtons & 6))
	{
		buttontaken --;
		updatetime = 0;
	}
	else
	{
		updatetime = 0;
	}
}

void							CELL_PAD_Reset						()
{
	SDL_ResetKeyboard();
	CELL_ReleaseAllMouseButtons();

	cellPadClearBuf(0);
	CELL_Video.Pads.Pads[0].OldButtons = 0xFFFFFFFF;
}

/******* Functions to process game pad buttons in mouse and keyboard mode ********/
void							CELL_PAD_SetFixedMap				(const CELL_FixedMap* aMap)
{
	CELL_Video.OSK.MappingOverride = aMap ? 1 : 0;

	CELL_PAD_Reset();

	if(aMap)
	{
		for(int j = 0; j != NUM_BUTTONS; j ++)
		{
			if(aMap->Buttons[j] != HID_NOEVENT)
			{
				for(int i = 0; i != OSK_KEY_MAP_SIZE; i ++)
				{
					if(CELL_OSK_Map[i].HIDID == aMap->Buttons[j])
					{
						CELL_Video.Pads.ButtonMap[j].Mapped = &CELL_OSK_Map[i];
						break;
					}
				}
			}
			else
			{
				CELL_Video.Pads.ButtonMap[j].Mapped = 0;
			}
		}
	}
	else
	{
		for(int i = 0; i != NUM_BUTTONS; i ++)
		{
			CELL_Video.Pads.ButtonMap[i].Mapped = CELL_Video.Pads.ButtonMap[i].RealMapped;
		}
	}
}

/* Process gamepad button aButton, aNew is the new state of the button, aOld is the old state of the button */
void							CELL_PAD_ProcessButton				(uint32_t aButton, uint32_t aNew, uint32_t aOld)
{
	/* Bail early */
	if((aNew && aOld) || (!aNew && !aOld) || aButton >= NUM_BUTTONS || CELL_Video.Pads.ButtonMap[aButton].Nullify || !CELL_Video.Pads.ButtonMap[aButton].Mapped)
	{
		return;
	}

	/* Handle mouse */
	if(CELL_Video.Pads.ButtonMap[aButton].Mapped->MouseButton)
	{
		CELL_DoMouseEvent(CELL_Video.Pads.ButtonMap[aButton].Mapped->MouseButton, aNew ? SDL_PRESSED : SDL_RELEASED);
	}

	/* Handle key */
	else
	{
		CELL_DoKeyEventSDLK(CELL_Video.Pads.ButtonMap[aButton].Mapped->HIDID, aNew ? SDL_PRESSED : SDL_RELEASED, CELL_Video.Pads.ButtonMap[aButton].Mapped->Character);
	}
}
#endif

/********** Implementation ********/
void							CELL_PAD_SetOpen					(SDL_Joystick* aJoystick, uint32_t aIndex, uint32_t aState)
{
	/* Called by SDL_Joystick to open the pad, if the pad isn't open it won't send events */
	if(aIndex < NUM_PADS)
	{
		CELL_Video.Pads.Pads[aIndex].Open = aState;
		CELL_Video.Pads.Pads[aIndex].Joystick = aJoystick;

		/* Don't reset the pad if we are using this pad for mouse/keyboard */
#ifndef CELL_NO_SOFT_INPUT
		if(aState && CELL_Video.Pads.Pads[aIndex].Mode == JOYSTICK)
#endif
		{
			CELL_Video.Pads.Pads[aIndex].OldButtons = 0;
			for(int i = 0; i != NUM_AXES; i ++)
			{
				CELL_Video.Pads.Pads[aIndex].OldAxes[i] = 0x80;
			}

			cellPadClearBuf(aIndex);
		}
	}
}

#ifndef CELL_NO_SOFT_INPUT
void							CELL_PAD_SetMode					(uint32_t aIndex, enum PadMode aMode)
{
	if(aIndex == 0 && (aMode == KEYBOARD || aMode == MOUSE || aMode == JOYSTICK))
	{
		/* Leave old mode */
		ModeExit[(int)CELL_Video.Pads.Pads[0].Mode](aMode);

		/* Enter new mode */
		ModeEnter[(int)aMode](CELL_Video.Pads.Pads[0].Mode);

		/* Update Status */
		CELL_Video.Pads.Pads[aIndex].Mode = aMode;

		CELL_Video.Pads.Pads[aIndex].OldButtons = 0;
		for(int i = 0; i != NUM_AXES; i ++)
		{
			CELL_Video.Pads.Pads[aIndex].OldAxes[i] = 0x80;
		}
	}
}

int								CELL_PAD_DrawOverlay				()
{
	/* In mouse or joystick mode, draw the icon if nessicary */
	if(CELL_Video.Pads.InputIconHideTime > SDL_GetTicks())
	{
		DrawInputIcons();
	}

	/* Draw the keyboard */
	if(CELL_Video.Pads.Pads[0].Mode == KEYBOARD)
	{
		CELL_PAD_KEYBOARD_Draw();
		CELL_CAPTION_Draw();
	}

	return 0;
}
#endif

void							CELL_PAD_ProcessByMode				()
{
	InitPads();

#ifndef CELL_NO_SOFT_INPUT
	/* We only process keyboard and mouse here, SDL_joystick will tell us when to do joystick mode */
	if(CELL_Video.Pads.Pads[0].Mode == KEYBOARD || CELL_Video.Pads.Pads[0].Mode == MOUSE)
	{
		/* Get appropiate functions by mode */
		void (FASTCALL*axes)(int32_t, int32_t) = (CELL_Video.Pads.Pads[0].Mode == KEYBOARD) ? CELL_PAD_KEYBOARD_Axes : CELL_PAD_MOUSE_Axes;
		void (FASTCALL*buttons)(uint32_t, uint32_t) = (CELL_Video.Pads.Pads[0].Mode == KEYBOARD) ? CELL_PAD_KEYBOARD_Buttons : CELL_PAD_MOUSE_Buttons;

		CellPadData data;
		if(CELL_OK == cellPadGetData(0, &data) && data.len >= 8)
		{
			uint32_t newbuttons = data.button[2] | (data.button[3] << 8);

			buttons(newbuttons, CELL_Video.Pads.Pads[0].OldButtons);
			CELL_Video.Pads.Pads[0].OldButtons = newbuttons;

			/* Update the axis values now, we don't care if they are different than last time */
			CELL_Video.Pads.Pads[0].OldAxes[0] = data.button[6];
			CELL_Video.Pads.Pads[0].OldAxes[1] = data.button[7];
			CELL_Video.Pads.Pads[0].OldAxes[2] = data.button[4];
			CELL_Video.Pads.Pads[0].OldAxes[3] = data.button[5];
		}

		/* Move the cursor */
		axes(CELL_Video.Pads.Pads[0].OldAxes[0], CELL_Video.Pads.Pads[0].OldAxes[1]);
	}
	/* Update the buttons if the joystick isn't open, prevents getting stuck in joystick mode */
	else if(CELL_Video.Pads.Pads[0].Mode == JOYSTICK && !CELL_Video.Pads.Pads[0].Open)
	{
		CellPadData data;
		if(CELL_OK == cellPadGetData(0, &data) && data.len >= 8)
		{
			CELL_Video.Pads.Pads[0].OldButtons = data.button[2] | (data.button[3] << 8);;
		}
	}

	/* Update Mode */
	UpdateMode();
#endif
}

